﻿(function () {
    var initializing = false,
            fnTest = /xyz/.test(function () { xyz; }) ? /\b_super\b/ : /.*/;
    this.Class = function () { };
    Class.extend = function (prop) {
        var _super = this.prototype;
        initializing = true;
        var prototype = new this();
        initializing = false;
        for (var name in prop) {
            prototype[name] = typeof prop[name] == "function" && typeof _super[name] == "function" && fnTest.test(prop[name]) ?
                    (function (name, fn) {
                        return function () {
                            var tmp = this._super;
                            this._super = _super[name];
                            var ret = fn.apply(this, arguments);
                            this._super = tmp;
                            return ret;
                        };
                    })(name, prop[name]) : prop[name];
        }
        function Class() {
            if (!initializing && this.create)
                this.create.apply(this, arguments);
        }
        Class.prototype = prototype;
        Class.constructor = Class;
        Class.extend = arguments.callee;
        return Class;
    };
})();

var Point = Class.extend({
    x: 0,
    y: 0,
    create: function (x, y) {
        this.x = x;
        this.y = y;
    },
    move: function (dx, dy) {
        this.x += dx;
        this.y += dy;
    },
    moveTo: function (x, y) {
        this.x = x;
        this.y = y;
    },
    equals: function (point) {
        return this.x == point.x && this.y == point.y;
    },
    toString: function () {
        return "(" + this.x + ", " + this.y + ")";
    }
});

var Rect = Class.extend({
    left: 0,
    top: 0,
    right: 0,
    bottom: 0,
    create: function (left, top, right, bottom) {
        this.left = left;
        this.top = top
        this.right = right;
        this.bottom = bottom;
    },
    width: function () {
        return this.right - this.left;
    },
    height: function () {
        return this.bottom - this.top;
    },
    move: function (dx, dy) {
        this.left += dx;
        this.right += dx;
        this.top += dy;
        this.bottom += dy;
    },
    moveTo: function (x, y) {
        this.right = x + this.width();
        this.bottom = y + this.height();
        this.left = x;
        this.top = y;
    },
    isPointIn: function (point) {
        return this.left <= point.x && this.right > point.x && this.top <= point.y && this.bottom > point.y;
    },
    isOverlap: function (rc) {
        return !(this.left >= rc.right || this.right <= rc.left || this.top >= rc.bottom || this.bottom <= rc.top);
    },
    equals: function (rc) {
        return this.left == rc.left && this.top == rc.top && this.right == rc.right && this.bottom == rc.bottom;
    },
    toString: function () {
        return "[" + this.left + ", " + this.top + ", " + this.right + ", " + this.bottom + "]";
    }
});

var Widget = Class.extend({
    id: null,
    parent: null,
    children: null,
    offsetRect: null,
    canvas: null,
    create: function (id, parent, canvas, option) {
        this.id = id || "_id_" + Math.random();
        this.parent = parent;
        this.canvas = canvas || (this.parent ? this.parent.canvas : null);
        this.children = new Array();
        this.offsetRect = new Rect(0, 0, 0, 0);
        this.option = option;
        this.layout = option.layout;
        this.style = option.style;

        if (this.parent != null)
            this.parent.addChild(this);

    },
    init: function () {
        this.eachChild(function (el) {
            el.onPaint();
        });
    },
    onMouseDown: function (point) {
        this.eachChild(function (el) {
            if (el.hitTest(point)) {
                el.onMouseDown(point);
                return true;
            } else {
                return false;
            }
        }, true);
    },
    onMouseUp: function (point) {
        this.eachChild(function (el) {
            if (el.hitTest(point)) {
                el.onMouseUp(point);
                return true;
            } else {
                return false;
            }
        }, true);
    },
    onMouseMove: function (point) {
        this.eachChild(function (el) {
            return el.onMouseMove(point);
        }, true);
    },
    onPaint: function (canvas) { },
    addChild: function (child) {
        if (!this.hasChild(child)) {
            this.children.push(child);
        }
        child.parent = this;
    },
    hasChild: function (child) {
        for (var i = 0; i < this.children.length; i++) {
            if (this.children[i] == child)
                return true;
        }
        return false;
    },
    eachChild: function (callback, reverse) {
        if (reverse) {
            for (var i = this.children.length - 1; i >= 0; i--) {
                if (callback(this.children[i]))
                    break;
            }
        } else {
            for (var i = 0; i < this.children.length; i++) {
                if (callback(this.children[i]))
                    break;
            }
        }
    },
    moveChildToTop: function (child) {
        child = this.removeChild(child);
        if (child != null)
            this.children.push(child);
    },
    removeChild: function (child) {
        for (var i = 0; i < this.children.length; i++) {
            if (this.children[i] == child)
                return this.children.splice(i, 1)[0];
        }
    },
    hitTest: function (point) {
        return this.offsetRect.isPointIn(point);
    },
    show: function () {
        this.redraw();
    },
    redraw: function () {
        this.onPaint();
        this.eachChild(function (el) { el.redraw(); });
    },
    onDestroy: function () {
        this.eachChild(function (el) { el.onDestroy(); });
        this.children = [];
        if (this.parent != null)
            this.parent.removeChild(this);
    },
    toString: function () {
        return this.id;
    }
});

var RootWidget = Widget.extend({
    create: function (id, canvas, option) {
        this._super(id, null, canvas, option);
    },
    init: function () {
        var _this = this;
        this.canvas.onmousemove = this.canvas.ontouchmove = function (e) {
            e = e || window.event;
            _this.onMouseMove(_this.getFixedMousePoint(e, _this.canvas));
        };
        this.canvas.onmousedown = this.canvas.ontouchstart = function (e) {
            e = e || window.event;
            _this.onMouseDown(_this.getFixedMousePoint(e, _this.canvas));
        };
        this.canvas.onmouseup = this.canvas.ontouchend = function (e) {
            e = e || window.event;
            _this.onMouseUp(_this.getFixedMousePoint(e, _this.canvas));
        };
        this.getFixedMousePoint = function (e, dom) {
            var x = e.pageX - dom.offsetLeft;
            var y = e.pageY - dom.offsetTop;
            return new Point(x, y);
        };
        document.onkeydown = function (e) {
            e = e || window.event; _this.onKeyDown(e);
        }
        document.onkeyup = function (e) {
            e = e || window.event; _this.onKeyUp(e);
        }
    }
});

var Board = RootWidget.extend({
    campOrder: 1,
    mover: 0,
    selectedChessman: null,
    create: function (id, canvas, option) {
        this._super(id, canvas, option);
    },
    init: function () {
        this.offsetRect.left = 0;
        this.offsetRect.top = 0;
        this.offsetRect.right = 460;
        this.offsetRect.bottom = 510;
        this._super();
    },
    findChess: function (pos) {
        if (!this.isValidPos(pos))
            return null;
        for (var i = 0; i < this.children.length; i++) {
            var child = this.children[i];
            if (child instanceof Chess && child.pos != null && child.pos.equals(pos))
                return child;
        }
        return null;
    },
    moveChess: function (chess, pos) {
        this.removeChess(pos);
        chess.pos = pos;
        this.mover = 1 - chess.camp;
    },
    removeChess: function (pos) {
        var chess = this.findChess(pos);
        if (chess != null)
            this.removeChild(chess);
    },
    isValidPos: function (pos) {
        return pos != null && pos.x >= 0 && pos.x < 9 && pos.y >= 0 && pos.y < 10;
    },
    isInsideCamp: function (pos, camp) {
        if (!this.isValidPos(pos))
            return false; if (camp == this.campOrder) { return pos.y <= 4; } else { return pos.y >= 5; }
    },
    isInsidePalace: function (pos, camp) {
        if (!this.isValidPos(pos))
            return false;
        if (pos.x < 3 || pos.x > 5)
            return false;
        if (camp == this.campOrder) {
            return pos.y <= 2;
        } else { return pos.y >= 7; }
    },
    onPaint: function () {
        var ctx = this.canvas.getContext('2d');
        ctx.fillStyle = this.style.board.background;

        ctx.beginPath();
        ctx.rect(0, 0, this.layout.offsetWidth, this.layout.offsetHeight);
        ctx.fill();
        ctx.closePath();

        var p = this.layout.padding, s = this.layout.cell, w = this.layout.width, h = this.layout.height;
        ctx.strokeStyle = this.style.board.border;
        ctx.lineWidth = 2;
        ctx.beginPath();
        for (var i = 0; i < 10; i++) {
            ctx.moveTo(p, s * i + p);
            ctx.lineTo(w + p, s * i + p);
        }

        ctx.moveTo(p, p);
        ctx.lineTo(p, h + p);
        ctx.moveTo(w + p, p);
        ctx.lineTo(w + p, h + p);
        for (var i = 1; i < 8; i++) {
            ctx.moveTo(s * i + p, p);
            ctx.lineTo(s * i + p, s * 4 + p);
            ctx.moveTo(s * i + p, s * 5 + p);
            ctx.lineTo(s * i + p, h + p);
        }
        ctx.moveTo(s * 3 + p, p);
        ctx.lineTo(s * 5 + p, s * 2 + p);
        ctx.moveTo(s * 5 + p, 0 + p);
        ctx.lineTo(s * 3 + p, s * 2 + p);
        ctx.moveTo(s * 3 + p, s * 7 + p);
        ctx.lineTo(s * 5 + p, s * 9 + p);
        ctx.moveTo(s * 5 + p, s * 7 + p);
        ctx.lineTo(s * 3 + p, s * 9 + p);
        ctx.stroke();
        ctx.closePath();

        ctx.font = this.style.board.font;
        ctx.fillStyle = this.style.board.border;
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillText("楚河", p + s * 2, p + s * 4.5);
        ctx.fillText("漢界", p + s * 6, p + s * 4.5);
    },
    onMouseDown: function (point) {
        if (this.selectedChessman != null) {
            var chessPos = this.point2chessPos(point);
            if (this.selectedChessman.isTargetValid(chessPos)) {
                this.selectedChessman.moveTo(chessPos);
                this.moveChess(this.selectedChessman, chessPos);
                this.selectChessman(null);
            }
        }
        else {
            this._super(point);
        }
    },
    onMouseUp: function (point) {
        this._super(point);
    },

    point2chessPos: function (point) {
        return new Point(Math.round((point.x - this.layout.padding) / this.layout.cell), Math.round((point.y - this.layout.padding) / this.layout.cell));
    },
    selectChessman: function (chessman) {
        var oldSelectedChessman = this.selectedChessman;
        if (oldSelectedChessman != null) {
            oldSelectedChessman.isSelected = false;
        }
        this.selectedChessman = chessman;
        if (this.selectedChessman != null) {
            this.selectedChessman.isSelected = true;
        }
    }
});

var Chess = Widget.extend({
    name: null,
    camp: null,
    pos: null,
    isSelected: false,
    targetPos: null,
    targetIndicatorAlpha: 0.2,
    create: function (id, parent, name, camp, pos, option) {
        this._super(id, parent, null, option);
        this.name = name;
        this.camp = camp || 0;
        this.pos = pos || new Point(0, 0);

        this.offsetRect.left = this.layout.padding + this.layout.cell * this.pos.x - this.layout.cell / 2;

        this.offsetRect.top = this.layout.padding + this.layout.cell * this.pos.y - this.layout.cell / 2;
        this.offsetRect.right = this.offsetRect.left + this.layout.cell;
        this.offsetRect.bottom = this.offsetRect.top + this.layout.cell;

    },
    onPaint: function () {
        var ctx = this.canvas.getContext('2d');
        ctx.fillStyle = this.style.chess[this.camp].background;
        ctx.strokeStyle = this.style.chess[this.camp].border;
        ctx.font = this.style.chess[this.camp].font;
        var x = this.offsetRect.left + this.layout.cell / 2,
                    y = this.offsetRect.top + this.layout.cell / 2;

        ctx.beginPath();
        ctx.fillStyle = "rgba(0, 0, 0, 0.2)";
        if (this.isSelected) {
            ctx.fillStyle = "rgba(255, 0, 0, 0.5)"
            ctx.arc(x + 4, y + 8, this.layout.chessRadius + 1, 0, 360);
        }
        else {
            ctx.arc(x + 1, y + 2, this.layout.chessRadius + 1, 0, 360);
        }
        ctx.fill();
        ctx.fillStyle = this.style.chess[this.camp].background;
        ctx.closePath();
        if (this.targetPos != null && this.targetIndicatorAlpha > 0) {
            ctx.beginPath();
            ctx.fillStyle = "rgba(0, 128, 0, " + this.targetIndicatorAlpha + ")";
            ctx.arc(this.layout.padding + this.targetPos.x * this.layout.cell, this.layout.padding + this.targetPos.y * this.layout.cell, this.layout.cell / 2, 0, 360);
            ctx.fill();
            ctx.fillStyle = this.style.chess[this.camp].background;
            ctx.closePath();
        }
        ctx.beginPath();
        ctx.arc(x, y, this.layout.chessRadius, 0, 360);
        ctx.fill();
        ctx.textAlign = "center";
        ctx.textBaseline = "middle";
        ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
        ctx.fillText(this.name, x + 1, y - this.layout.fontSize / 16 + 1);
        ctx.fillStyle = this.style.chess[this.camp].fontColor;
        ctx.fillText(this.name, x, y - this.layout.fontSize / 16);
        ctx.stroke();
        ctx.closePath();
    },
    onMouseDown: function (point) {
        if (this.parent.mover == this.camp) {
            this.parent.selectChessman(this);
            this.parent.moveChildToTop(this);
            this.parent.redraw();
        }
    },

    chessPos2point: function (x, y) { },
    moveTo: function (pos) {
        var left = this.layout.padding + this.layout.cell * pos.x - this.layout.cell / 2,
                    top = this.layout.padding + this.layout.cell * pos.y - this.layout.cell / 2;
        var dx = left - this.offsetRect.left,
                    dy = top - this.offsetRect.top;
        var t = 0, c = 15, _this = this;
        var timer = setInterval(function () {
            if (++t > c) {
                clearInterval(timer); _this.pos = pos;
                _this.offsetRect.moveTo(left, top);
                _this.targetPos = null;
                _this.targetIndicatorAlpha = 0.2;
                return;
            }
            var ratio = 0; if (t <= c / 2) {
                ratio = 2 * t / c; ratio = 1 - 0.5 * ratio * ratio * ratio * ratio;
            } else {
                ratio = 2 - 2 * t / c; ratio = 0.5 * ratio * ratio * ratio * ratio;
            }
            _this.offsetRect.moveTo(left - dx * ratio, top - dy * ratio);
            _this.targetIndicatorAlpha = 0.2 * ratio;
            _this.parent.redraw();
        }, 40);
    },
    isTargetValid: function (pos) {
        if (!this.parent.isValidPos(pos))
            return false;
        var chess = this.parent.findChess(pos);
        return chess == null || chess.camp != this.camp;
    }
});

var Chariot = Chess.extend({
    create: function (id, parent, camp, pos, option) {
        this._super(id, parent, "車", camp, pos, option);
    },

    isTargetValid: function (pos) {
        if (!this._super(pos))
            return false;
        var dx = pos.x - this.pos.x, dy = pos.y - this.pos.y;
        if (dx != 0 && dy != 0)
            return false;
        var targetChess = this.parent.findChess(pos);
        var steps = Math.max(Math.abs(dx), Math.abs(dy));
        var blockPos = new Point(this.pos.x, this.pos.y);
        for (var i = 1; i < steps; i++) {
            blockPos.x += dx / steps; blockPos.y += dy / steps;
            if (this.parent.findChess(blockPos) != null)
                return false;
        }
        return true;
    }
});

var Horse = Chess.extend({
    create: function (id, parent, camp, pos, option) {
        this._super(id, parent, "馬", camp, pos, option);
    },

    isTargetValid: function (pos) {
        if (!this._super(pos))
            return false;
        var dx = pos.x - this.pos.x,
                        dy = pos.y - this.pos.y;
        if (dx == 0 || dy == 0 || Math.abs(dx) + Math.abs(dy) != 3)
            return false;
        var targetChess = this.parent.findChess(pos);
        var blockPos = new Point(this.pos.x, this.pos.y);
        if (Math.abs(dx) == 2)
            blockPos.x += dx / 2;
        else
            blockPos.y += dy / 2;
        return this.parent.findChess(blockPos) == null;
    }
});

var Elephant = Chess.extend({
    create: function (id, parent, camp, pos, option) {
        this._super(id, parent, camp == 0 ? "相" : "象", camp, pos, option);
    },
    isTargetValid: function (pos) {
        if (!this._super(pos))
            return false;
        if (!this.parent.isInsideCamp(pos, this.camp))
            return false;
        var dx = pos.x - this.pos.x,
                        dy = pos.y - this.pos.y;
        if (Math.abs(dx) != 2 || Math.abs(dy) != 2)
            return false;
        var blockPos = new Point(this.pos.x + dx / 2, this.pos.y + dy / 2);
        return this.parent.findChess(blockPos) == null;
    }
});

var Guard = Chess.extend({
    create: function (id, parent, camp, pos, option) {
        this._super(id, parent, camp == 0 ? "士" : "仕", camp, pos, option);
    },

    isTargetValid: function (pos) {
        if (!this._super(pos))
            return false;
        if (!this.parent.isInsidePalace(pos, this.camp))
            return false;
        var dx = pos.x - this.pos.x, dy = pos.y - this.pos.y;
        if (Math.abs(dx) != 1 || Math.abs(dy) != 1)
            return false;
        return true;
    }
});

var General = Chess.extend({
    create: function (id, parent, camp, pos, option) {
        this._super(id, parent, camp == 0 ? "帥" : "將", camp, pos, option);
    },

    isTargetValid: function (pos) {
        if (!this._super(pos))
            return false;
        if (!this.parent.isInsidePalace(pos, this.camp))
            return false;
        var dx = pos.x - this.pos.x, dy = pos.y - this.pos.y;
        if (Math.abs(dx) + Math.abs(dy) != 1)
            return false;
        return true;
    }
});

var Cannon = Chess.extend({
    create: function (id, parent, camp, pos, option) {
        this._super(id, parent, camp == 0 ? "炮" : "砲", camp, pos, option);
    },

    isTargetValid: function (pos) {
        if (!this._super(pos))
            return false;
        var dx = pos.x - this.pos.x, dy = pos.y - this.pos.y;
        if (dx != 0 && dy != 0)
            return false;
        var targetChess = this.parent.findChess(pos);
        var steps = Math.max(Math.abs(dx), Math.abs(dy));
        var blockPos = new Point(this.pos.x, this.pos.y);
        var blocks = 0; for (var i = 1; i < steps; i++) {
            blockPos.x += dx / steps; blockPos.y += dy / steps;
            if (this.parent.findChess(blockPos) != null)
                blocks++;
        }
        return (blocks == 0 && targetChess == null) || (blocks == 1 && targetChess != null);
    }
});

var Pawn = Chess.extend({
    create: function (id, parent, camp, pos, option) {
        this._super(id, parent, camp == 0 ? "兵" : "卒", camp, pos, option);
    },
    isTargetValid: function (pos) {
        if (!this._super(pos))
            return false;
        var dx = pos.x - this.pos.x, dy = pos.y - this.pos.y;
        if (this.parent.isInsideCamp(pos, this.camp) && dx != 0)
            return false;
        if (this.camp == this.parent.campOrder && dy < 0)
            return false;
        else if (this.camp != this.parent.campOrder && dy > 0)
            return false;
        if (Math.abs(dx) + Math.abs(dy) != 1)
            return false;
        return true;
    }
});
function ChessGame(boardId) {
    var option = {
        layout: {
            padding: 30,
            cell: 50,
            chessRadius: 20,
            fontSize: 36,
            width: 400,
            height: 450,
            offsetWidth: 460,
            offsetHeight: 510,
            refresh: function (cellUnit) {
                this.cell = cellUnit;
                this.chessRadius = Math.round(cellUnit * 0.4);
                this.padding = this.chessRadius + 10;
                this.fontSize = (this.chessRadius - 3) * 2;
                this.width = cellUnit * 8;
                this.height = cellUnit * 9;
                this.offsetWidth = this.width + this.padding * 2;
                this.offsetHeight = this.height + this.padding * 2;
            }
        },
        style: {
            board: { border: "#630", background: "#fed", font: "36px 隶书" },
            chess: [
                        { border: "#fa8", background: "#fc9", font: "36px 隶书", fontColor: "#c00" },
                        { border: "#fa8", background: "#fc9", font: "36px 隶书", fontColor: "#090" }
                    ],
            refresh: function (font) {
                this.board.font = font;
                this.chess[0].font = font;
                this.chess[1].font = font;
            }
        },
        refresh: function(cellUnit) {
            this.layout.refresh(cellUnit);
            var font = this.layout.fontSize + "px 隶书";
            this.style.refresh(font);
        }
    };

    var GameManager = Class.extend({});
    this.init = function (boardId) {
        var height = document.documentElement.clientHeight - 110;
        var width = document.documentElement.clientWidth - 20;
        var cellUnit = Math.floor((height - 20) / 9.8);
        option.refresh(cellUnit);
        var vertialMargin = (height - option.layout.offsetHeight) / 2;
        var horizontalMargin = (width - option.layout.offsetWidth) / 2;

        var canvas = document.getElementById(boardId);
        canvas.setAttribute("width", option.layout.offsetWidth);
        canvas.setAttribute("height", option.layout.offsetHeight);
        canvas.style.margin = "" + vertialMargin + " " + horizontalMargin + " " + vertialMargin + " " + horizontalMargin;

        var board = new Board("chessBoard", canvas, option);
        createChesses(board);
        board.init();
        board.show();
        window.board = board;
        $(window).resize(function () {
            //alert(this.board);
            alert(window.board);
            window.board.init();
        });
    }
    function createChesses(board) {
        {
            (new Chariot("車01", board, 0, new Point(0, 9), option));
            (new Chariot("車02", board, 0, new Point(8, 9), option));
            (new Horse("馬01", board, 0, new Point(1, 9), option));
            (new Horse("馬02", board, 0, new Point(7, 9), option));
            (new Elephant("相01", board, 0, new Point(2, 9), option));
            (new Elephant("相02", board, 0, new Point(6, 9), option));
            (new Guard("士01", board, 0, new Point(3, 9), option));
            (new Guard("士02", board, 0, new Point(5, 9), option));
            (new General("帥00", board, 0, new Point(4, 9), option));
            (new Pawn("兵01", board, 0, new Point(0, 6), option));
            (new Pawn("兵02", board, 0, new Point(2, 6), option));
            (new Pawn("兵03", board, 0, new Point(4, 6), option));
            (new Pawn("兵04", board, 0, new Point(6, 6), option));
            (new Pawn("兵05", board, 0, new Point(8, 6), option));
            (new Cannon("炮01", board, 0, new Point(1, 7), option));
            (new Cannon("炮02", board, 0, new Point(7, 7), option));
        }
        {
            (new Chariot("車11", board, 1, new Point(0, 0), option));
            (new Chariot("車12", board, 1, new Point(8, 0), option));
            (new Horse("馬11", board, 1, new Point(1, 0), option));
            (new Horse("馬12", board, 1, new Point(7, 0), option));
            (new Elephant("象11", board, 1, new Point(2, 0), option));
            (new Elephant("象12", board, 1, new Point(6, 0), option));
            (new Guard("仕11", board, 1, new Point(3, 0), option));
            (new Guard("仕12", board, 1, new Point(5, 0), option));
            (new General("將10", board, 1, new Point(4, 0), option));
            (new Pawn("卒11", board, 1, new Point(0, 3), option));
            (new Pawn("卒12", board, 1, new Point(2, 3), option));
            (new Pawn("卒13", board, 1, new Point(4, 3), option));
            (new Pawn("卒14", board, 1, new Point(6, 3), option));
            (new Pawn("卒15", board, 1, new Point(8, 3), option));
            (new Cannon("砲11", board, 1, new Point(1, 2), option));
            (new Cannon("砲12", board, 1, new Point(7, 2), option));
        }
    }
    this.init(boardId);
}